package cloud.health.common.file.domain;

import com.google.common.primitives.Bytes;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class Request {

    /**
     * http请求
     */
    private Http http;

    /**
     * 上下文
     */
    private ChannelHandlerContext ctx;

    /**
     * 请求头
     */
    private CopyOnWriteArrayList<Byte> header;

    /**
     * 请求体
     */
    private CopyOnWriteArrayList<Byte> body;

    /**
     * 已接收请求体长度
     */
    private long ACCEPT_BODY_LENGTH = 0L;

    /**
     * 接收
     */
    public void accept(ByteBuf buf) {
        ByteBuf copy = buf.copy();
        resetIfNewRequest(copy);
        if (Objects.isNull(http)) {
            parseHeader(buf);
            return;
        }
        try {
            byte[] bytes = new byte[copy.readableBytes()];
            copy.readBytes(bytes);
            ACCEPT_BODY_LENGTH += bytes.length;
            body.addAll(Bytes.asList(bytes));
        } finally {
            copy.release();
        }
    }

    /**
     * 获取body字节
     */
    public byte[] fetch() {
        if (Objects.isNull(body) || body.isEmpty()) {
            return null;
        }
        byte[] bytes = Bytes.toArray(body);
        body.clear();
        return bytes;
    }

    /**
     * 当前长度
     */
    public long getCurrentLength() {
        return ACCEPT_BODY_LENGTH;
    }

    /**
     * 总长度
     */
    public long getTotalLength() {
        Long len = Long.parseLong(http.getHeaders().getOrDefault("Content-Length", "0").toString());
        return len;
    }

    /**
     * http
     */
    public Http getHttp() {
        return http;
    }

    /**
     * 上下文
     */
    public ChannelHandlerContext getContext() {
        return ctx;
    }

    /**
     * 初始化请求
     */
    public static Request init(ChannelHandlerContext ctx) {
        Request it = new Request();
        it.ctx = ctx;
        it.header = new CopyOnWriteArrayList<>();
        it.body = new CopyOnWriteArrayList<>();
        return it;
    }

    /**
     * 请求是否结束
     */
    public boolean completed() {
        return getCurrentLength() == getTotalLength();
    }

    /**
     * 解析请求头
     */
    private void parseHeader(ByteBuf buf) {
        ByteBuf copy = buf.copy();
        try {
            byte[] bytes = new byte[copy.readableBytes()];
            copy.readBytes(bytes);
            int index = 0;
            for (int i = bytes.length - 1; i > 3; i--) {
                if ((byte) 10 == bytes[i] && (byte) 13 == bytes[i - 1] && (byte) 10 == bytes[i - 2] && (byte) 13 == bytes[i - 3]) {
                    index = i;
                }
            }
            if (index == 0) {
                header.addAll(Bytes.asList(bytes));
            } else {
                List<Byte> list = Bytes.asList(bytes);
                if (list.size() - 1 > index) {
                    body.addAll(list.subList(index + 1, list.size()));
                    ACCEPT_BODY_LENGTH += body.size();
                }
                header.addAll(list.subList(0, index + 1));
                http = Http.build(Unpooled.wrappedBuffer(Bytes.toArray(header)));
                header.clear();
            }
        } finally {
            copy.release();
        }
    }

    /**
     * 重置请求
     */
    private void resetIfNewRequest(ByteBuf buf) {
        String s = buf.toString(StandardCharsets.UTF_8).toLowerCase();
        if (validate(s)) {
            http = null;
            header.clear();
            body.clear();
            ACCEPT_BODY_LENGTH = 0L;
        }
    }

    /**
     * 是否新请求
     */
    private static boolean validate(String s) {
        String pattern = "(get|post|put|patch|delete|copy|head|options|link|unlink|purge|lock|unlock|propfind|view)\\s.*?\\shttp.*?[\r]{0,}\n";
        Pattern r = Pattern.compile(pattern);
        Matcher m = r.matcher(s);
        return m.find();
    }

    @Data
    public static class Http implements Serializable {
        private String uri;
        private String method;
        private String version;
        private Map<String, Object> headers;

        public static Http build(ByteBuf buf) {
            try {
                String str = buf.toString(StandardCharsets.UTF_8);
                List<String> lines = new ArrayList<>(Arrays.asList(str.split("\n")));
                String s = lines.get(0);
                String[] sg = s.split(" ");
                lines.remove(0);
                Http it = new Http();
                it.uri = sg[1].trim();
                it.method = sg[0].trim();
                it.version = sg[2].trim();
                it.headers = new ConcurrentHashMap<>();
                lines.parallelStream().forEach(item -> {
                    String[] t = item.split(":");
                    if (StringUtils.isBlank(t[0])) {
                        return;
                    }
                    if (t.length == 2) {
                        it.headers.put(t[0].trim(), t[1].trim());
                    } else {
                        it.headers.put(t[0].trim(), "");
                    }
                });
                return it;
            } finally {
                buf.release();
            }
        }

        public String getContentType() {
            return headers.getOrDefault("Content-Type", "").toString();
        }
    }
}
